/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       creature_bank.h

	$Header: /game/creature_bank.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "creature_bank.h"

#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "army.h"
#include "artifact_effect.h"
#include "artifact_properties.h"
#include "artifact_slot.h"
#include "artifact_type.h"
#include "basic_dialog.h"
#include "combat_context_adv_object.h"
#include "combat_context_ptr.h"
#include "combat_result.h"
#include "creature.h"
#include "creature_bank_type.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "default_artifacts.h"
#include "estimate_outcome.h"
#include "hero_keyword_replacer.h"
#include "material_names.h"
#include "object_registration.h"
#include "player.h"
#include "mouse_event.h"
#include "music.h"
#include "random.h"
#include "random_monster_experience.h"
#include "replace_keywords.h"
#include "run_combat.h"
#include "simple_dialog.h"
#include "table.h"
#include "table_ptr.h"

// ------------------------------------------------------------------------
// traits of creature banks
// ------------------------------------------------------------------------
namespace 
{
	int const k_current_save_version = 4;

	// -------------------------------------------------------------------
	// Translate t_creature_bank's save file version to t_creature_array's version
	// -------------------------------------------------------------------


	t_object_registration<t_creature_bank> registration( k_adv_object_creature_bank );

	enum t_reward_artifact_type
	{
		k_reward_parchment_1,
		k_reward_parchment_2,
		k_reward_potion,
		k_reward_item,
		k_reward_scroll_1,
		k_reward_parchment_3,
		k_reward_scroll_2,
		k_reward_parchment_4,
		k_reward_scroll_3,
		k_reward_treasure,
		k_reward_parchment_5,
		k_reward_scroll_4,
		k_reward_minor,
		k_reward_scroll_5,
		k_reward_major,

		k_reward_count
	};

	struct t_bank_traits
	{
		std::vector<t_creature_type> creatures;
		std::vector<int>			 initial;
		std::vector<int>             per_day;
		t_material_array             initial_reward_ratio;
		t_material_array			 added_reward_ratio;
		int                          initial_artifact_ratio;
		int							 added_artifact_ratio;
		int                          total_initial_value;
		int							 artifact_limit[k_reward_count];
		int                          total_artifact_limit;
	};

	class t_bank_traits_table
	{
	public:
		t_bank_traits_table();
		
		t_bank_traits const& operator[] (t_creature_bank_type type) const;
	protected:
		t_bank_traits m_traits[k_creature_bank_type_count];
	};

	inline t_bank_traits const& t_bank_traits_table::operator[] (t_creature_bank_type type) const
	{
		return m_traits[type];
	}

	enum
	{
		k_keyword_column,
		k_initial_column,
		k_per_day_column,
		k_creature_column,
		k_initial_2_column,
		k_per_day_2_column,
		k_creature_2_column,
		k_initial_3_column,
		k_per_day_3_column,
		k_creature_3_column,
		k_initial_gold_column,
		k_initial_wood_column,
		k_initial_ore_column,
		k_initial_crystal_column,
		k_initial_sulfur_column,
		k_initial_mercury_column,
		k_initial_gems_column,
		k_initial_artifacts_column,
		k_added_gold_column,
		k_added_wood_column,
		k_added_ore_column,
		k_added_crystal_column,
		k_added_sulfur_column,
		k_added_mercury_column,
		k_added_gems_column,
		k_added_artifacts_column,
		k_parchments_column,
		k_scrolls_column,
		k_potions_column,
		k_items_column,
		k_treasures_column,
		k_minor_column,
		k_major_column,
		k_total_artifacts_column
	};
};

double const k_experience_to_gold_ratio = 3;

// ------------------------------------------------------------------------
// traits of creature banks
// ------------------------------------------------------------------------
t_bank_traits_table::t_bank_traits_table()
{
	t_table_cache			cache( "creature_banks" );
	t_table_ptr				table = cache.get();
	t_table::const_iterator line = table->begin();
	t_creature_bank_type	type;
	t_creature_type			creature;
	int                     i;
	int                     amount;
	double                  added;
	int                     total;
	int						parchment_limit;
	int						scroll_limit;

	line++;
	line++;
	for (; line != table->end(); line++)
	{
		t_string_vector const& data = *line;

		if (data[0].empty())
			continue;
		type = get_creature_bank_type( data[k_keyword_column] );
		if (type < 0)
			continue;

		t_bank_traits& traits = m_traits[type];

		for (i = 0; i < 3; i++)
		{
			creature = get_creature( data[k_creature_column + i * 3] );
			if (creature == k_creature_none)
				continue;
			traits.creatures.push_back( creature );
			amount = atoi( data[k_initial_column + i * 3].c_str() );
			traits.initial.push_back( amount );
			added = atof( data[k_per_day_column + i * 3].c_str() );
			amount = int( added * 256 );
			traits.per_day.push_back( amount );
		}
		for (i = 0; i < k_material_count; i++)
		{
			traits.initial_reward_ratio[i] = atoi( data[k_initial_gold_column + i].c_str() );
			traits.added_reward_ratio[i]   = atoi( data[k_added_gold_column + i].c_str() );
		}
		traits.initial_artifact_ratio = atoi( data[k_initial_artifacts_column].c_str() );
		traits.added_artifact_ratio = atoi( data[k_added_artifacts_column].c_str() );
		parchment_limit = atoi( data[k_parchments_column].c_str() );
		scroll_limit = atoi( data[k_scrolls_column].c_str() );
		traits.artifact_limit[k_reward_parchment_1] = parchment_limit;
		traits.artifact_limit[k_reward_parchment_2] = parchment_limit;
		traits.artifact_limit[k_reward_parchment_3] = parchment_limit;
		traits.artifact_limit[k_reward_parchment_4] = parchment_limit;
		traits.artifact_limit[k_reward_parchment_5] = parchment_limit;
		traits.artifact_limit[k_reward_scroll_1]	= scroll_limit;
		traits.artifact_limit[k_reward_scroll_2]	= scroll_limit;
		traits.artifact_limit[k_reward_scroll_3]	= scroll_limit;
		traits.artifact_limit[k_reward_scroll_4]	= scroll_limit;
		traits.artifact_limit[k_reward_scroll_5]	= scroll_limit;
		traits.artifact_limit[k_reward_potion]		= atoi( data[k_potions_column].c_str() );
		traits.artifact_limit[k_reward_item]		= atoi( data[k_items_column].c_str() );
		traits.artifact_limit[k_reward_treasure]	= atoi( data[k_treasures_column].c_str() );
		traits.artifact_limit[k_reward_minor]		= atoi( data[k_minor_column].c_str() );
		traits.artifact_limit[k_reward_major]		= atoi( data[k_major_column].c_str() );
		traits.total_artifact_limit = atoi( data[k_total_artifacts_column].c_str() );

		// compute total initial value
		total = 0;
		for (i = 0; i < (int)traits.creatures.size(); i++)
			total += get_traits( traits.creatures[i] ).experience * traits.initial[i];
		total = (int)(total * k_experience_to_gold_ratio + 1000);
		traits.total_initial_value = total;
	}
}


static t_bank_traits const& get_traits( t_creature_bank_type type )
{
	static t_bank_traits_table const traits;

	return traits[type];
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
t_creature_bank::t_creature_bank( t_stationary_adventure_object const& source )
               : t_stationary_adventure_object( source )
{

	m_type = t_creature_bank_type( get_major_subtype() );
	m_restocking_delay = 0;
	m_reward_value = 0;

	t_creature_pair      pair;
	t_bank_traits const& traits = get_traits( m_type );
	int                  i;

	for (i = 0; i < (int)traits.creatures.size(); i++)
	{
		pair.creature = traits.creatures[i];
		pair.amount   = 0;
		m_creatures.push_back( pair );
	}
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                                   t_direction direction, t_adventure_frame* frame )
{
	int player_number = army->get_owner_number();

	if (player_number < 0)
		return;

	t_sound_cache music = get_music_playing();
	if (!army->get_owner()->is_computer())
	{
		stop_music();
		get_dialog_sound( k_dialog_sound_creature_bank )->play( get_sound_volume() );
	}

	// note that this player has visited.
	if (player_number >= (int)m_visited.size())
		m_visited.insert( m_visited.end(), player_number - m_visited.size() + 1, false );

	bool is_human = !army->get_owner()->is_computer();
	m_visited[player_number] = true;
	if (empty())
	{
		if (is_human)
			ok_dialog( get_text( "empty" ), get_dialog_bottom( point ) );
		if (!army->get_owner()->is_computer())
			play_music( music );
		return;
	}

	std::string text;
	t_hero_keyword_replacer replacer;
	if (is_human)
	{
		t_skill_mastery information = get_information_level();

		replacer.add_creatures( *this, information < k_mastery_advanced );
		text = replacer( get_text( "initial" ) );

		t_counted_ptr<t_basic_dialog> dialog = new t_basic_dialog;

		if (information >= k_mastery_expert)
			text += "  " + get_outcome_text( *this );
		dialog->set_text( text );
		dialog->add_creatures( *this, information < k_mastery_advanced );
		dialog->add_cancel_button();
		dialog->add_ok_button();
		dialog->set_title( get_name() );
		dialog->open( get_dialog_bottom(point), true, k_align_bottom );
		if (!dialog->run_modal())
		{
			if (!army->get_owner()->is_computer())
				play_music( music );
			return;
		}

		if (!army->get_owner()->is_computer())
			play_music( music );
	}

	// create army
	t_combat_context_ptr	context;

	context = new t_combat_context_adv_object( army, this, point );

	run_combat( *context );
}

void t_creature_bank::on_combat_end( t_army* army, t_combat_result result, 
									 t_creature_array* losses, t_adventure_frame* frame )
{
	frame->update_army( army );
	if (attacker_lost( result ))
		return;

	// distribute the goods.
	t_player*  player = army->get_owner();

	player->gain( m_materials );
	for (int i = 0; i < (int)m_artifacts.size(); i++)
		army->add( m_artifacts[i] );
	
	if (!player->is_computer())
	{
		t_counted_ptr<t_basic_dialog> dialog = new t_basic_dialog;
		t_hero_keyword_replacer replacer;
		std::string				text;
	
		replacer.add_creatures( losses[true], true ); 
		replacer.add_artifacts( m_artifacts );
		replacer.add_materials( m_materials );
		for (int i = 0; i < t_material_array::k_size; i++)
		{
			if (m_materials[i] > 0)
				dialog->add_material( t_material(i), m_materials[i] );
		}
		for (int i = 0; i < (int)m_artifacts.size(); i++)
			dialog->add_artifact( m_artifacts[i] );
		text = replacer( get_text( "defeat" ) );
		dialog->set_text( text );
		dialog->add_ok_button();
		dialog->set_title( get_name() );
		dialog->open( get_dialog_bottom( army->get_position() ), true, k_align_bottom );
		dialog->run_modal();
	}

	clear();
	m_artifacts.clear();
	m_materials.set( 0 );
	m_restocking_delay = 28;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::initialize( t_adventure_map& map )
{
	t_stationary_adventure_object::initialize( map );
	reset();
	add_value( random(0,35), map );
	t_creature_array::initialize();
}

// ---------------------------------------------------------------
// preplacement initialization
// ---------------------------------------------------------------
bool t_creature_bank::preplacement( t_adventure_map& map, int pass )
{
	return false;
}

void t_creature_bank::read_postplacement( t_adventure_map& map )
{
	t_stationary_adventure_object::read_postplacement( map );
	t_creature_array::read_postplacement( map );
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::reset()
{
	int                  i;
	t_bank_traits const& traits = get_traits( m_type );

	clear();
	for (i = 0; i < (int)traits.creatures.size(); i++)
	{
		m_creatures[i].amount = 0;
		add( m_creatures[i].creature, traits.initial[i] );
	}
	m_reward_value = traits.total_initial_value;
	m_visited.clear();
	set_reward();
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::process_new_day()
{
	if (m_restocking_delay > 0)
	{
		m_restocking_delay--;
		return;
	}
	add_value( 1, *get_map() );
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::right_click( t_mouse_event const& event,
								      t_adventure_frame* adventure_frame )
{
	bool			visited = false;
	int				player = get_map()->get_player_number();
	std::string		text;
	t_skill_mastery information = get_information_level();

	// note that this player has visited.
	if (player >= (int)m_visited.size())
		m_visited.insert( m_visited.end(), player - m_visited.size() + 1, false );
	if (information >= k_mastery_basic)
		m_visited[player] = true;

	if (player < (int)m_visited.size())
    visited = (m_visited[player] == 0 ? false : true);
	if (!visited)
		text = get_text( "help" );
	else if (empty())
		text = get_text( "help_empty" );
	else
	{
		t_hero_keyword_replacer replacer;

		replacer.add_creatures( *this, information < k_mastery_advanced );
		text = replacer( get_text( "help_visited" ) );
		if (information >= k_mastery_expert)
			text += "  " + get_outcome_text( *this );
	}
	show_popup_text( text, event.screen_point );
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
namespace
{

	int const k_reward_value[] = 
	{
		 250, // k_reward_parchment_1
		 450, // k_reward_parchment_2
		 500, // k_reward_potion
		 500, // k_reward_item
		 500, // k_reward_scroll_1
		 800, // k_reward_parchment_3
		 900, // k_reward_scroll_2
		1500, // k_reward_parchment_4
		1600, // k_reward_scroll_3
		2000, // k_reward_treasure
		2500, // k_reward_parchment_5
		3000, // k_reward_scroll_4
		4000, // k_reward_minor
		5000, // k_reward_scroll_5
		8000, // k_reward_major
	};
};

// ------------------------------------------------------------------------
// determine type of artifact for reward purposes
// ------------------------------------------------------------------------
t_reward_artifact_type get_artifact_type( t_artifact const& artifact, bool separate_potions )
{
	t_artifact_type type = artifact.get_icon();

	if (get_slot( type ) == k_artifact_slot_potion && separate_potions)
		return k_reward_potion;
	if (type == k_artifact_parchment)
	{
		t_artifact_effect_list const& effects = artifact.get_effects();
		t_artifact_effect const&      effect = *effects.front();

		switch (effect.get_level())
		{
			case 1:
				return k_reward_parchment_1;
			case 2:
				return k_reward_parchment_2;
			case 3:
				return k_reward_parchment_3;
			case 4:
				return k_reward_parchment_4;
			case 5:
				return k_reward_parchment_5;
		}
	}
	if (type == k_artifact_scroll)
	{
		t_artifact_effect_list const& effects = artifact.get_effects();
		t_artifact_effect const&      effect = *effects.front();

		switch (effect.get_level())
		{
			case 1:
				return k_reward_scroll_1;
			case 2:
				return k_reward_scroll_2;
			case 3:
				return k_reward_scroll_3;
			case 4:
				return k_reward_scroll_4;
			case 5:
				return k_reward_scroll_5;
		}
	}
	switch (artifact.get_level())
	{
		case k_artifact_level_item:
			return k_reward_item;
		case k_artifact_level_treasure:
			return k_reward_treasure;
		case k_artifact_level_minor:
			return k_reward_minor;
		case k_artifact_level_major:
			return k_reward_major;
	}
	return k_reward_major;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// create artifacts based on a total value
// ------------------------------------------------------------------------
int t_creature_bank::create_artifacts( int awarded )
{
	t_bank_traits const& traits = get_traits( m_type );
	int					 artifacts[k_reward_count];
	int					 total_weight = 0;
	int                  change;
	int                  total_value;
	int 				 total;
	int                  remainder = awarded;
	bool                 change_made;
	int                  i;

	// compute rough distribution of artifacts
	for (i = 0; i < k_reward_count; i++)
	{
		artifacts[i] = 0;
	}

	do
	{
		total_weight = 0;
		total = 0;
		total_value = 0;
		// compute rough distribution of artifacts
		for (i = 0; i < k_reward_count; i++)
		{
			if (traits.artifact_limit[i] > artifacts[i])
				total_weight += k_reward_value[i];
		}
		// distribute points, but limit to amount per catagory
		change_made = false;
		for (i = k_reward_count - 1; i >= 0; i--)
		{
			if (traits.artifact_limit[i] > artifacts[i])
			{
				change = remainder / total_weight;
				if (change > traits.artifact_limit[i] - artifacts[i])
					change = traits.artifact_limit[i] - artifacts[i];
				if (change > 0)
				{
					artifacts[i] += change;
					remainder -= change * k_reward_value[i];
					change_made = true;
				}
				total_weight -= k_reward_value[i];
			}
			total += artifacts[i];
			total_value += artifacts[i] * k_reward_value[i];
		}
		remainder = awarded - total_value;
	} while (change_made && remainder > 0);

	int total_limit = traits.total_artifact_limit;
	int removed_item = -1;

	// loop until we have reduced the total to within the overall limit, and 
	// we've allocated all our rewards (unlikely, usually there will be a remainder)
	while (total > total_limit || remainder > 0)
	{
		// if we have too many artifacts, eliminate some from the cheapest upwards
		if (total >= total_limit)
		{
			for (removed_item = 0; removed_item < k_reward_count; removed_item++)
			{
				if (artifacts[removed_item] > 0)
					break;
			}
			if (removed_item == k_reward_count)
				break;
			change = total - total_limit;
			if (change < 1)
				change = 1;
			if (change > artifacts[removed_item])
				change = artifacts[removed_item];
			artifacts[removed_item] -= change;
			total -= change;
			remainder += k_reward_value[removed_item] * change;
		}
		// if we can afford to add an artifact, and have a remainder left over, add one.
		if (total < total_limit)
		{
			for (i = k_reward_count - 1; i >= 0; i--)
				if (k_reward_value[i] <= remainder && traits.artifact_limit[i] < artifacts[i])
					break;
			// didn't find one, so exit.
			if (i < 0)
				break;
			artifacts[i]++;
			total++;
			remainder -= k_reward_value[i];
			// added the same artifact we just removed, so exit.
			if (i <= removed_item)
				break;
		}
	}
	create_artifacts( artifacts );
	return awarded - remainder;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// create artifacts from an array of artifact counts
// ------------------------------------------------------------------------
void t_creature_bank::create_artifacts( int* artifacts )
{
	// phew.  Finally computed what rewards we want.  Now compare them to existing 
	// artifacts, and create or destroy them as needed.
	t_bank_traits const& traits = get_traits( m_type );
	int                    i;
	int					   totals[k_reward_count];
	t_reward_artifact_type type;
    bool                   separate_potions = traits.artifact_limit[k_reward_potion] > 0;

	// count existing artifacts, and destroy excess
	memset( totals, 0, sizeof(totals));
	for (i = 0; i < (int)m_artifacts.size(); )
	{
		type = get_artifact_type( m_artifacts[i], separate_potions );
		if (totals[type] < artifacts[type])
		{
			totals[type]++;
			i++;
			continue;
		}
		get_map()->reuse( m_artifacts[i].get_icon() );
		m_artifacts.erase( m_artifacts.begin() + i );
	}

	t_artifact_type artifact_type;
	t_spell         spell;

	// create new artifacts
	for (i = 0; i < k_reward_count; i++)
	{
		while (totals[i] < artifacts[i])
		{
			switch (i)
			{
				case k_reward_potion:
					artifact_type = get_map()->get_random_potion();
					m_artifacts.push_back( get_default_artifact( artifact_type ) );
					break;
				case k_reward_item:
					artifact_type = get_map()->get_random_artifact( k_artifact_level_item );
					m_artifacts.push_back( get_default_artifact( artifact_type ));
					break;
				case k_reward_treasure:
					artifact_type = get_map()->get_random_artifact( k_artifact_level_treasure );
					m_artifacts.push_back( get_default_artifact( artifact_type ));
					break;
				case k_reward_minor:
					artifact_type = get_map()->get_random_artifact( k_artifact_level_minor );
					m_artifacts.push_back( get_default_artifact( artifact_type ));
					break;
				case k_reward_major:
					artifact_type = get_map()->get_random_artifact( k_artifact_level_major );
					m_artifacts.push_back( get_default_artifact( artifact_type ));
					break;
				case k_reward_parchment_1:
					// spell = get_map()->get_random_parchment( 1 );
          spell = k_spell_haste;
					m_artifacts.push_back( get_parchment( spell ));
					break;
				case k_reward_parchment_2:
					spell = get_map()->get_random_parchment( 2 );
					m_artifacts.push_back( get_parchment( spell ));
					break;
				case k_reward_parchment_3:
					spell = get_map()->get_random_parchment( 3 );
					m_artifacts.push_back( get_parchment( spell ));
					break;
				case k_reward_parchment_4:
					spell = get_map()->get_random_parchment( 4 );
					m_artifacts.push_back( get_parchment( spell ));
					break;
				case k_reward_parchment_5:
					spell = get_map()->get_random_parchment( 5 );
					m_artifacts.push_back( get_parchment( spell ));
					break;
				case k_reward_scroll_1:
					spell = get_map()->get_random_parchment( 1 );
					m_artifacts.push_back( get_scroll_artifact( spell ));
					break;
				case k_reward_scroll_2:
					spell = get_map()->get_random_parchment( 2 );
					m_artifacts.push_back( get_scroll_artifact( spell ));
					break;
				case k_reward_scroll_3:
					spell = get_map()->get_random_parchment( 3 );
					m_artifacts.push_back( get_scroll_artifact( spell ));
					break;
				case k_reward_scroll_4:
					spell = get_map()->get_random_parchment( 4 );
					m_artifacts.push_back( get_scroll_artifact( spell ));
					break;
				case k_reward_scroll_5:
					spell = get_map()->get_random_parchment( 5 );
					m_artifacts.push_back( get_scroll_artifact( spell ));
					break;
			}
			totals[i]++;
		}
	}
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::add_value( int days, t_adventure_map& map )
{
	t_bank_traits const& traits = get_traits( m_type );
	int                  i;
	int                  added;
	bool                 revise_value = false;

	for (i = 0; i < (int)m_creatures.size(); i++)
	{
		m_creatures[i].amount += (int)(traits.per_day[i] * days
								* get_monster_difficulty( map.get_player_difficulty() ));
		if (m_creatures[i].amount >= 256)
		{
			added = m_creatures[i].amount >> 8;
			add( m_creatures[i].creature, added );
			m_creatures[i].amount &= 0xFF;
			m_reward_value += (int)(get_traits( m_creatures[i].creature ).experience * added * k_experience_to_gold_ratio);
			revise_value = true;
		}
	}
	if (revise_value)
		set_reward();
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
void t_creature_bank::set_reward()
{
	t_bank_traits const& traits = get_traits( m_type );

	int i;
	int initial_reward = traits.total_initial_value;
	int total_initial_weight;
	int total_added_weight;
	int awarded;
	int change;
	int added_reward = m_reward_value - initial_reward;

	// compute how much to award for artifacts
	total_initial_weight = traits.initial_artifact_ratio + traits.initial_reward_ratio.get_sum();
	total_added_weight = traits.added_artifact_ratio + traits.added_reward_ratio.get_sum();
	if (traits.total_artifact_limit > 0)
	{
		// figure total value allocated to artifacts
		awarded = traits.initial_artifact_ratio * initial_reward / total_initial_weight;
		awarded += traits.added_artifact_ratio * added_reward / total_added_weight;
		awarded = create_artifacts( awarded );
		change = awarded * initial_reward / (initial_reward + added_reward);
		initial_reward -= change;
		added_reward -= awarded - change;
	}

	total_initial_weight = traits.initial_reward_ratio.get_sum();
	total_added_weight = traits.added_reward_ratio.get_sum();

	for (i = k_material_count - 1; i >= 0; i--)
	{
		if (initial_reward + added_reward <= 0)
			break;

		awarded = 0;
		if (total_initial_weight > 0)
			awarded += initial_reward * traits.initial_reward_ratio[i] / total_initial_weight;
		if (total_added_weight > 0)
			awarded += added_reward * traits.added_reward_ratio[i] / total_added_weight;
		awarded = awarded / k_material_value[i];
		m_materials[i] = awarded;
		awarded *= k_material_value[i];
		change = awarded * initial_reward / (initial_reward + added_reward);
		initial_reward -= change;
		added_reward -= awarded - change;
		total_initial_weight -= traits.initial_reward_ratio[i];
		total_added_weight -= traits.added_reward_ratio[i];
	}
	m_materials[k_gold] = (m_materials[k_gold] / 250) * 250;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
bool t_creature_bank::write( std::streambuf& stream ) const
{
	int i;

	put<t_creature_bank_type>( stream, m_type );
	t_creature_array::write( stream );
	put<t_uint8>( stream, m_creatures.size() );
	for (i = 0; i < (int)m_creatures.size(); i++)
		::write( stream, &m_creatures[i], sizeof(t_creature_pair));
	put<t_uint32>( stream, m_reward_value );
	put<t_uint8>( stream, m_visited.size() );
	if (m_visited.size() > 0)
		::write( stream, &(*m_visited.begin()), m_visited.size() );
	m_materials.write( stream );
	put<t_uint8>( stream, m_artifacts.size() );
	for (i = 0; i < (int)m_artifacts.size(); i++)
		m_artifacts[i].write( stream );
	put<t_uint8>( stream, m_restocking_delay );
	return true;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
int t_creature_bank::get_version() const
{
	return k_current_save_version;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
bool t_creature_bank::read( std::streambuf&						stream, 
						    t_qualified_adv_object_type const&	type,
						    int									version ) 
{
	int i;
	int count;

	m_type = get<t_creature_bank_type>( stream );
	if (version >= 1)
	{
		if (version < 3)
		{
			if (!t_creature_array::read_version( stream, 0 ))
				return false;
		}
		else if (version < 4)
		{
			if (!t_creature_array::read_version( stream, 1 ))
				return false;
		} else if (!t_creature_array::read( stream ))
			return false;
	}
	count = get<t_uint8>( stream );
	m_creatures.clear();
	for (i = 0; i < count; i++)
		m_creatures.push_back( get<t_creature_pair>( stream ));
	if (version >= 2)
	{
		m_reward_value = get<t_uint32>( stream );
		count = get<t_uint8>( stream );
		m_visited.resize( count );
		if (count > 0)
			::read( stream, &(*m_visited.begin()), m_visited.size() );
	}
	else
	{
		m_reward_value = get_traits( m_type ).total_initial_value;
	}
	m_materials.read( stream );
	count = get<t_uint8>( stream );
	for (i = 0; i < count; i++)
	{
		m_artifacts.push_back( t_artifact() );
		m_artifacts[i].read( stream );
	}
	m_restocking_delay = get<t_uint8>( stream );
	return true;
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
float t_creature_bank::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	if (empty())
		return 0.0f;
	else
	{
		t_owned_ptr<t_creature_array> temp_army = ai_copy_of_creature_array( *this );
		t_ai_army ai_army( army.get_map(), army.get_owner() );
		ai_army.swap_creatures( *temp_army.get() );

		float value_lost = ::value_lost_in_combat( &army, &ai_army, true );
		if (value_lost >= k_lost_value_death_threshold)
			return 0.0f;

		float value = army.ai_value() * -value_lost;

		t_bank_traits const& traits = get_traits( m_type );

		// I'm not scaling this for demand as it can be allocated as pretty much any 
		// resource type.
		return value + traits.total_initial_value;
	}
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
float t_creature_bank::ai_activation_value_drop( t_creature_array const& army ) const
{
	if (empty())
	{
		return 0.0f;
	}
	else
	{
		t_owned_ptr<t_creature_array> temp_army = ai_copy_of_creature_array( *this );
		t_ai_army ai_army( army.get_map(), army.get_owner() );
		ai_army.swap_creatures( *temp_army.get() );

		return ::value_lost_in_combat( &army, &ai_army, true );
	}
}

// ------------------------------------------------------------------------
// fight creatures for a reward
// ------------------------------------------------------------------------
t_skill_mastery t_creature_bank::get_anti_stealth_level() const
{
	return t_creature_array::get_anti_stealth_level();
}
